package com.easipass.unifiedconfig;

import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import com.easipass.unifiedconfig.NotFoundException.Part;
import com.easipass.unifiedconfig.core.Authentication;
import com.easipass.unifiedconfig.core.ConfigDomain;

/**
 * 
 * @version $Id$
 * @author itea
 * @date 2010-1-28
 */
public class DefaultSession implements Session {

	/** domain : authentication */
	private final Map<String, Authentication> authIdx = new HashMap<String, Authentication>();
	
	private final ConfigManager configManager;
	
	public DefaultSession(ConfigManager configManager) {
		super();
		this.configManager = configManager;
	}

	public synchronized boolean authenticate(String domain, String user, String password)
		throws NotFoundException {
		ConfigDomain cdomain = configManager.getConfigDomain(domain);
		if(cdomain == null)
			throw new NotFoundException(Part.DOMAIN, domain);
		Authentication a = cdomain.authenticate(user, password);
		if(a == null) return false;
		authIdx.put(a.getDomain(), a);
		return true;
	}
	
	public Collection<Authentication> getAuthentications() {
		return authIdx.values();
	}
	
	public Parameter getParameter(String uci) 
		throws MalformedUCIException, NotFoundException, InsufficientPrivillageException {
		return getParameter(new UCI(uci));
	}
	
	public Parameter getParameter(UCI uci) 
		throws NotFoundException, InsufficientPrivillageException {
		ConfigDomain cdomain = configManager.getConfigDomain(uci.getDomain());
		if(cdomain == null)
			throw new NotFoundException(Part.DOMAIN, uci);
		
		Authentication a = authIdx.get(uci.getDomain());
		return a == null ? cdomain.getParameter(uci) : cdomain.getParameter(a, uci);
	}
	
	public void setParameter(Parameter p)
		throws NotFoundException, InsufficientPrivillageException {
		ConfigDomain cdomain = configManager.getConfigDomain(p.getUci().getDomain());
		if(cdomain == null)
			throw new NotFoundException(Part.DOMAIN, p.getUci());
		Authentication a = authIdx.get(p.getUci().getDomain());
		if(a == null)
			throw new InsufficientPrivillageException(p.getUci());
		
		cdomain.setParameter(a, p);
	}
	
	public void removeParameter(UCI uci)
		throws NotFoundException, InsufficientPrivillageException {
		ConfigDomain cdomain = configManager.getConfigDomain(uci.getDomain());
		if(cdomain == null)
			throw new NotFoundException(Part.DOMAIN, uci);
		Authentication a = authIdx.get(uci.getDomain());
		if(a == null)
			throw new InsufficientPrivillageException(uci);
		
		cdomain.removeParameter(a, uci);
	}
	
	public void deleteParameterValues(UCI uci, String... values)
			throws NotFoundException, InsufficientPrivillageException {
		ConfigDomain cdomain = configManager.getConfigDomain(uci.getDomain());
		if(cdomain == null)
			throw new NotFoundException(Part.DOMAIN, uci);
		Authentication a = authIdx.get(uci.getDomain());
		if(a == null)
			throw new InsufficientPrivillageException(uci);
		
		cdomain.deleteParameterValues(a, uci, values);
	}

	public void insertParameterValues(UCI uci, String... values)
			throws NotFoundException, InsufficientPrivillageException {
		ConfigDomain cdomain = configManager.getConfigDomain(uci.getDomain());
		if(cdomain == null)
			throw new NotFoundException(Part.DOMAIN, uci);
		Authentication a = authIdx.get(uci.getDomain());
		if(a == null)
			throw new InsufficientPrivillageException(uci);
		
		cdomain.insertParameterValues(a, uci, values);
	}

	public boolean existsParameter(UCI uci) {
		ConfigDomain cdomain = configManager.getConfigDomain(uci.getDomain());
		if(cdomain == null)
			return false;
		
		return cdomain.containsParameter(uci);
	}
	
	public Collection<UCI> listParameters(UCI range) 
	throws NotFoundException{
		if("*".equals(range.getDomain())) {
			LinkedList<UCI> ls = new LinkedList<UCI>();
			for(String d:configManager.listDomains()) {
				ConfigDomain cd = configManager.getConfigDomain(d);
				ls.addAll(cd.listParameters(range));
			}
			return ls;
		} else {
			ConfigDomain cdomain = configManager.getConfigDomain(range.getDomain());
			if(cdomain == null)
				throw new NotFoundException(Part.DOMAIN, range);
			
			return cdomain.listParameters(range);
		}
	}
	
	public void createAccount(UCI uci)
		throws NotFoundException, InsufficientPrivillageException {
		ConfigDomain cdomain = configManager.getConfigDomain(uci.getDomain());
		if(cdomain == null)
			throw new NotFoundException(Part.DOMAIN, uci.getDomain());
		Authentication a = authIdx.get(uci.getDomain());
		if(a == null)
			throw new InsufficientPrivillageException(uci.getDomain());
		
		cdomain.createAccount(a, uci.getAccount());
	}
	
	public void createModule(UCI uci)
		throws NotFoundException, InsufficientPrivillageException {
		ConfigDomain cdomain = configManager.getConfigDomain(uci.getDomain());
		if(cdomain == null)
			throw new NotFoundException(Part.DOMAIN, uci.getDomain());
		Authentication a = authIdx.get(uci.getDomain());
		if(a == null)
			throw new InsufficientPrivillageException(uci.getDomain());
		
		cdomain.createModule(a, uci.getAccount(), uci.getModule());
	}

	public boolean createUser(String domain, String user, String password)
			throws NotFoundException, InsufficientPrivillageException {
		ConfigDomain cdomain = configManager.getConfigDomain(domain);
		if(cdomain == null)
			throw new NotFoundException(Part.DOMAIN, domain);
		Authentication a = authIdx.get(domain);
		if(a == null)
			throw new InsufficientPrivillageException(domain);
		return cdomain.createUser(a, user, password);
	}

	public boolean changeUserPassword(String domain, String user,
			String newPassword) throws NotFoundException, InsufficientPrivillageException {
		ConfigDomain cdomain = configManager.getConfigDomain(domain);
		if(cdomain == null)
			throw new NotFoundException(Part.DOMAIN, domain);
		Authentication a = authIdx.get(domain);
		if(a == null)
			throw new InsufficientPrivillageException(domain);
		return cdomain.changeUserPassword(a, user, newPassword);
	}

	public boolean removeUser(String domain, String user)
			throws NotFoundException, InsufficientPrivillageException {
		ConfigDomain cdomain = configManager.getConfigDomain(domain);
		if(cdomain == null)
			throw new NotFoundException(Part.DOMAIN, domain);
		Authentication a = authIdx.get(domain);
		if(a == null)
			throw new InsufficientPrivillageException(domain);
		return cdomain.removeUser(a, user);
	}

	public boolean existsUser(String domain, String user)
	throws NotFoundException {
		ConfigDomain cdomain = configManager.getConfigDomain(domain);
		if(cdomain == null)
			throw new NotFoundException(Part.DOMAIN, domain);
		return cdomain.existsUser(user);
	}

	public List<String> listUsers(String domain)
	throws NotFoundException {
		ConfigDomain cdomain = configManager.getConfigDomain(domain);
		if(cdomain == null)
			throw new NotFoundException(Part.DOMAIN, domain);
		return cdomain.listUsers();
	}

	// ----
	
	public void grantModify(String domain, String user, UCI uci)
			throws InsufficientPrivillageException, NotFoundException {
		ConfigDomain cdomain = configManager.getConfigDomain(domain);
		if(cdomain == null)
			throw new NotFoundException(Part.DOMAIN, domain);
		Authentication a = authIdx.get(domain);
		if(a == null)
			throw new InsufficientPrivillageException(domain);
		
		cdomain.grantModify(a, user, uci);
	}

	public void grantSys(String domain, String user, String pri)
			throws InsufficientPrivillageException, NotFoundException {
		ConfigDomain cdomain = configManager.getConfigDomain(domain);
		if(cdomain == null)
			throw new NotFoundException(Part.DOMAIN, domain);
		Authentication a = authIdx.get(domain);
		if(a == null)
			throw new InsufficientPrivillageException(domain);
		
		cdomain.grantSys(a, user, pri);
	}

	public void revokeModify(String domain, String user, UCI uci)
			throws InsufficientPrivillageException, NotFoundException {
		ConfigDomain cdomain = configManager.getConfigDomain(domain);
		if(cdomain == null)
			throw new NotFoundException(Part.DOMAIN, domain);
		Authentication a = authIdx.get(domain);
		if(a == null)
			throw new InsufficientPrivillageException(domain);
		
		cdomain.revokeModify(a, user, uci);
	}

	public void revokeSys(String domain, String user, String pri)
			throws InsufficientPrivillageException, NotFoundException {
		ConfigDomain cdomain = configManager.getConfigDomain(domain);
		if(cdomain == null)
			throw new NotFoundException(Part.DOMAIN, domain);
		Authentication a = authIdx.get(domain);
		if(a == null)
			throw new InsufficientPrivillageException(domain);
		
		cdomain.revokeSys(a, user, pri);
	}
	
	public List<String> listModifyPrivillages(String domain, String user)
			throws NotFoundException, InsufficientPrivillageException {
		ConfigDomain cdomain = configManager.getConfigDomain(domain);
		if(cdomain == null)
			throw new NotFoundException(Part.DOMAIN, domain);
		Authentication a = authIdx.get(domain);
		if(a == null)
			throw new InsufficientPrivillageException(domain);
		return cdomain.listModifyPrivillages(a, user);
	}

	public List<String> listSysPrivillages(String domain, String user)
			throws NotFoundException, InsufficientPrivillageException {
		ConfigDomain cdomain = configManager.getConfigDomain(domain);
		if(cdomain == null)
			throw new NotFoundException(Part.DOMAIN, domain);
		Authentication a = authIdx.get(domain);
		if(a == null)
			throw new InsufficientPrivillageException(domain);
		return cdomain.listSysPrivillages(a, user);
	}

	public ConfigManager getConfigManager() {
		return configManager;
	}

}
